﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Health;

namespace PingHealthCheck
{
    public class PingHealthCheckRule : SPHealthAnalysisRule
    {
        private string _explanation = "";
        private SPHealthCheckErrorLevel _level = SPHealthCheckErrorLevel.Error;

        public override SPHealthCategory Category
        {
            get
            {
                return SPHealthCategory.Performance;
            }
        }

        public override SPHealthCheckStatus Check()
        {
            Debugger.Launch();
            try
            {
                StringBuilder sb = new StringBuilder();

                var notOK = 0;

                foreach( var t in ( SPFarm.Local.Servers.Where(t => t.Role == SPServerRole.Invalid).ToList() ) )
                {
                    var name = t.Name;
                    var isAlias = false;

                    //ManagementScope scope = new ManagementScope(@"\\" + System.Environment.GetEnvironmentVariable("computername") + @"\root\Microsoft\SqlServer\ComputerManagement");
                    //ManagementClass clientAlias = new ManagementClass(scope, new ManagementPath("SqlServerAlias"), null);
                    //clientAlias.Get();
                    var a = new Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer(".");
                    if( a.ServerAliases.Count > 0 )
                    {
                        foreach( Microsoft.SqlServer.Management.Smo.Wmi.ServerAlias b in a.ServerAliases )
                        {
                            if( string.Compare(b.Name, t.Name, StringComparison.InvariantCultureIgnoreCase) == 0 )
                            {
                                name = b.ServerName;
                                isAlias = true;
                                break;
                            }
                        }
                    }

                    var p = new System.Net.NetworkInformation.Ping();
                    var end = DateTime.Now.AddMinutes(10);
                    var total = 0;
                    var unavailable = 0;
                    var highLatency = 0;

                    while( DateTime.Now < end )
                    {
                        total++;
                        try
                        {
                            var r = p.Send(name);
                            if( r.Status == System.Net.NetworkInformation.IPStatus.Success )
                            {
                                if( r.RoundtripTime > 1 )
                                    highLatency++;
                            };
                        }
                        catch
                        {
                            unavailable++;
                        }

                        System.Threading.Thread.Sleep(1000);
                    };

                    var percent = ( ( ( (double)unavailable ) + ( (double)highLatency ) ) / ( (double)total ) ) * 100;

                    if( percent > 0.1 )
                    {
                        notOK++;
                        if( isAlias )
                        {
                            sb.AppendFormat("ERROR: Ping from current server to {0} (SQL alias for {5}):\r\n\tTotal PING count: {1}\r\n\tServer unavailable: {2} times\r\n\tLatency >= 1ms: {3} times = {4:0.##}%\r\n\r\n", t.Name, total, unavailable, highLatency, percent, name);
                        }
                        else
                        {
                            sb.AppendFormat("ERROR: Ping from current server to {0}:\r\n\tTotal PING count: {1}\r\n\tServer unavailable: {2} times\r\n\tLatency >= 1ms: {3} times = {4:0.##}%\r\n\r\n", t.Name, total, unavailable, highLatency, percent);
                        }
                        _level = SPHealthCheckErrorLevel.Error;
                    }
                    else
                    {
                        sb.AppendFormat("OK: Ping from current server to {0}:\r\n\tTotal PING count: {1}\r\n\tServer unavailable: {2} times\r\n\tLatency >= 1ms: {3} times = {4:0.##}%\r\n\r\n", t.Name, total, unavailable, highLatency, percent);
                        _level = SPHealthCheckErrorLevel.Information;
                    }
                };

                _explanation = sb.ToString();

                if( notOK > 0 )
                    return SPHealthCheckStatus.Failed;
                else
                    return SPHealthCheckStatus.Passed;
            }
            catch(Exception e)
            {
                _explanation = "Execution of health analyzer rule failed: \r\n" + e.Message + "\r\n\r\n" + e.StackTrace;
                _level = SPHealthCheckErrorLevel.RuleExecutionFailure;
                return SPHealthCheckStatus.Failed;
            }
        }

        public override SPHealthCheckErrorLevel ErrorLevel
        {
            get
            {
                return _level;
            }
        }

        public override string Explanation
        {
            get
            {
                return _explanation;
            }
        }

        public override string Remedy
        {
            get
            {
                return "Check your network configuration. Do you have a \"stretched farm\" architecture? Check it.";
            }
        }

        public override string Summary
        {
            get
            {
                return "Checks the latency of the network connection from each SharePoint server to all SQL Servers of the SharePoint farm.";
            }
        }

        public override SPHealthAnalysisRuleAutomaticExecutionParameters AutomaticExecutionParameters
        {
            get
            {
                SPHealthAnalysisRuleAutomaticExecutionParameters retval = new SPHealthAnalysisRuleAutomaticExecutionParameters();
                retval.Schedule = SPHealthCheckSchedule.Hourly;
                retval.Scope = SPHealthCheckScope.All;
                retval.ServiceType = typeof(SPTimerService);
                return retval;
            }
        }
    }
}
